package unit

import (
	"github.com/go-playground/validator/v10"
	"github.com/pkg/errors"
	"gopkg.in/gomail.v2"
	"net/smtp"
)

// EmailRequest 邮件发送请求参数
type EmailRequest struct {
	//	授权邮件服务器信息
	Host     string `json:"host"`     // 邮件服务请求地址
	Port     int    `json:"port"`     // 邮件服务请求端口
	SSL      bool   `json:"ssl"`      // 是否开启 SSL 验证，端口 465/587 默认开启
	Username string `json:"username"` // 邮件服务客户端授权账号
	Password string `json:"password"` // 邮件服务客户端授权密码
	// 基础信息
	Subject string   `json:"subject" validate:"required"`              // 邮件主题
	Type    string   `json:"type" validate:"required,oneof=text html"` // text-纯文本/html-HTML内容
	Text    string   `json:"text"`                                     // 纯文本邮件内容
	Html    string   `json:"html"`                                     // HTML 邮件内容
	Attach  []string `json:"attach"`                                   // 本地附件地址信息
	// 发件人信息
	FromName  string `json:"from_name" validate:"required"` // 发件人名称
	FromEmail string `json:"from_email"`                    // 发件人邮箱账号，默认与邮箱服务器授权账号相同
	// 收件人信息
	Tos []string `json:"tos" validate:"required"` // 收件人账号
	Cc  []string `json:"cc"`                      // 抄送人账号
	Bcc int      `json:"bcc"`                     // 是否邮件密送，0-默认/1-否/2-是，多账号推送时默认邮件密送
}

// Send 邮件发送核心方法
func (r *EmailRequest) Send() error {
	// 1. 验证参数
	if err := r.validate(); err != nil {
		return err
	}
	// 2. 初始化邮件发送请求器 && 初始化邮箱消息结构类
	Dialer := gomail.Dialer{
		Host:     r.Host,
		Port:     r.Port,
		Username: r.Username,
		Password: r.Password,
		Auth:     smtp.PlainAuth("", r.Username, r.Password, r.Host),
		SSL:      r.SSL || r.Port == 465,
	}
	m := gomail.NewMessage()
	// 3. 设置发件人邮箱地址、发件人名称
	if r.FromEmail == "" {
		r.FromEmail = r.Username
	}
	m.SetAddressHeader("From", r.FromEmail, r.FromName)
	// 4. 设置邮件主题
	m.SetHeader("Subject", r.Subject)
	// 5. 设置抄送用户
	if len(r.Cc) > 0 && r.Cc[0] != "" {
		m.SetHeader("Cc", r.Cc...)
	}
	// 6. 设置邮件内容
	if r.Type == "html" { // html 邮件
		m.SetBody("text/html", r.Html)
	} else { // 纯文本邮件
		m.SetBody("text/plain", r.Text)
	}
	// 7. 设置附件
	if len(r.Attach) > 0 {
		for _, file := range r.Attach {
			m.Attach(file)
		}
	}
	// 8. 设置密文或明文显示收件人
	bcc := "To" // 明文发送，能查看收件人
	if r.Bcc == 2 || (r.Bcc == 0 && len(r.Tos) > 1) {
		bcc = "Bcc" // 密文发送，所有人收件人都不显示
	}
	// 9. 执行推送
	m.SetHeader(bcc, r.Tos...)
	return Dialer.DialAndSend(m)
}

func (r *EmailRequest) validate() error {
	//	1. 基础验证
	if err := validator.New().Struct(r); err != nil {
		return err
	}
	// 2. 纯文本邮件参数验证
	if r.Type == "text" && r.Text == "" {
		return errors.New("text email content must exist")
	}
	// 3. HTML 邮件参数验证
	if r.Type == "html" && r.Html == "" {
		return errors.New("html email content must exist")
	}
	return nil
}
